package thread.print_ABC_by_turn;

//这种方法不需要为A、B、C写3次重复的代码，

public class Synchronized_ABC {

    public static void main(String[] args) throws Exception {
        Object a = new Object();
        Object b = new Object();
        Object c = new Object();
        ThreadPrinter pa = new ThreadPrinter("A", c, a);
        ThreadPrinter pb = new ThreadPrinter("B", a, b);
        ThreadPrinter pc = new ThreadPrinter("C", b, c);

        new Thread(pa).start();
        //sleep休眠的是主线程，所以必能保证3个线程按顺序运行
        Thread.sleep(10);// 保证初始ABC的启动顺序
        new Thread(pb).start();
        Thread.sleep(10);
        new Thread(pc).start();
        Thread.sleep(10);
    }
}

class ThreadPrinter implements Runnable {

    private String name;
    private Object prev;
    private Object self;

    public ThreadPrinter(String name, Object prev, Object self) {
        this.name = name;
        this.prev = prev;
        this.self = self;
    }



    @Override
    public void run() {
        int count = 2;
        while (count > 0) {// 多线程并发，不能用if，必须使用while循环
            synchronized (prev) { // 先获取 prev 锁
                synchronized (self) {// 再获取 self 锁
                    System.out.print(name);// 打印
                    count--;

                    self.notifyAll();// 唤醒其他线程竞争self锁，注意此时self锁并未立即释放。
                }
                // 此时执行完self的同步块，这时self锁才释放。

                try {
                    // 如果count==0,表示这是最后一次打印操作，通过notifyAll操作释放对象锁
                    if (count == 0) {
                        prev.notifyAll();
                    } else {
                        //如果不是最后一次打印，
                        //wait会释放锁，释放之后其他的线程就可以获取prev的锁了
                        //关键在于，notify会从wait阻塞的地方开始
                        prev.wait(); // 立即释放 prev锁，当前线程休眠，等待唤醒
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
